<?php
declare (strict_types = 1);
namespace Upload\Driver;
use Config;
use FTP\Connection;
use Upload\UploadInterface;
use Yaf\Exception;

class Ftp implements UploadInterface {
    /**
     * @var array
     */
    private array $config = [];

    /**
     * 上传错误信息
     * @var string
     */
    private string $error = '';

    /**
     * @var bool
     */
    private bool $debug  = true;

    /**
     * @var Connection|null
     */
    private ?Connection $connection  = null;

    /**
     * 上传模式 auto || ascii
     * @var string
     */
    private string $mode = 'auto';

    /**
     * 构造函数
     *
     * @param array $config
     * @throws Exception
     */
    public function __construct(array $config = array()) {
        try {
            $this->config = Config::get("ftp");
            $this->config = array_merge ( $this->config, $config );
            $this->mode = $this->config['mode']??$this->mode;
            $this->connect();
        } catch (\Exception $e) {
            throw new Exception($e->getMessage());
        }
    }

    /**
     * FTP连接
     *
     * @return boolean
     */
    public function connect(): bool
    {
        if ($this->config['ssl']===true) {
            $this->connection = ftp_ssl_connect($this->config['hostname'], (int)$this->config['port'], (int)$this->config['timeout']);
        }else {
            $this->connection = ftp_connect($this->config['hostname'], (int)$this->config['port'], (int)$this->config['timeout']);
        }
        if (false === ($this->connection)) {
            $this->error = "FTP连接失败";
            return false;
        }
        if( ! ftp_login($this->connection, $this->config['username'], $this->config['password'])) {
            $this->error = "FTP登录失败";
            return false;
        }

        if($this->config['passive'] === true) {
            ftp_pasv($this->connection, true);
        }

        return true;
    }

    /**
     * 创建目录
     *
     * @param string $savePath
     * @param int $permissions
     * @return boolean 创建状态，true-成功，false-失败
     */
    public function mkdir(string $savePath,int $permissions = 0755): bool
    {
        $dir = $this->config['rootPath'] . $savePath;
        if (is_dir ( $dir )) return true;
        if (mkdir ( $dir, $permissions, true )) {
            return true;
        } else {
            $this->error = "目录 {$savePath} 创建失败！";
            return false;
        }
    }

    /**
     * ftp创建多级目录
     * @param string $remote_file
     */
    private function createRemoteDir(string $remote_file): void
    {
        $remote_dir = dirname($remote_file);
        $path_arr = explode('/',$remote_dir); // 取目录数组
        $path_div = count($path_arr); // 取层数
        // 创建目录
        foreach($path_arr as $val) {
            if(!@ftp_chdir($this->conn, $val)) {
                $tmp = @ftp_mkdir($this->conn,$val);
                if(!$tmp) $this->error = "目录创建失败，请检查权限及路径是否正确！";return;
                @ftp_chmod($this->conn,0755,$val);
                @ftp_chdir($this->conn,$val);
            }
        }
        for($i=0;$i<$path_div;$i++) {
            @ftp_cdup($this->conn);
        }
    }

    /**
     * 上传
     *
     * @param string $localPath
     * @return boolean
     */
    public function upload(array|string $file = []): bool
    {
        $localPath = $file['file_path'];
        if( ! file_exists($localPath)) {
            $this->error = "文件不存在:".$localPath;
            return false;
        }
        $mode = ($this->mode == 'ascii') ? FTP_ASCII : FTP_BINARY;
        $this->createRemoteDir($this->config['remotePath']);
        $result = ftp_put($this->connection, $this->config['remotePath'], $localPath, $mode);
        if($result === false) {
            $this->error = "上传失败";
            return false;
        }
        return true;
    }

    /**
     *  修改文件名
     * @param string $old_file 旧文件/文件夹名
     * @param string $new_file 新文件/文件夹名
     */
    public function rename(string $old_file='',string $new_file=''): bool
    {
        $result = @ftp_rename($this->conn,$old_file,$new_file);
        if ($result === false){
            $this->error = "移动失败";
            return false;
        }
        return true;
    }

    /**
     * 删除文件
     *
     * @access  public
     * @param string $file 文件标识(ftp)
     * @return boolean
     */
    public function delete_file(string $file): bool
    {
        $result = ftp_delete($this->connection, $file);
        if($result === false) {
            $this->error = "文件删除失败：".$file;
            return false;
        }

        return true;
    }

    /**
     * 删除文件夹
     *
     * @access  public
     * @param string $path 目录标识(ftp)
     * @return boolean
     */
    public function delete_dir(string $path): bool
    {
        //对目录宏的'/'字符添加反斜杠'\'
        $path = preg_replace("/(.+?)\/*$/", "\\1/", $path);
        //获取目录文件列表
        $fileList = $this->fileList($path);
        if($fileList !== false AND count($fileList) > 0) {
            foreach($fileList as $item) {
                $this->delete_dir($item);
            }
        }
        //删除文件夹(空文件夹)
        $result = ftp_rmdir($this->connection, $path);
        if($result === false) {
            $this->error = "文件夹删除失败：".$path;
            return false;
        }

        return true;
    }

    /**
     * 获取目录文件列表
     *
     * @access  public
     * @param string $path 目录标识(ftp)
     * @return bool|array
     */
    public function fileList(string $path = '.'): bool|array
    {
        return ftp_nlist($this->connection, $path);
    }

    /**
     * 关闭FTP
     *
     * @return boolean
     */
    public function close(): bool
    {
        $this->connection = null;
        if ($this->hasFtpConnection()) {
            return @ftp_close($this->connection);
        }else {
            return false;
        }
    }

    /**
     * 获取最后一次上传错误信息
     *
     * @return string 错误信息
     */
    public function getError(): string
    {
        return $this->error;
    }

    private function hasFtpConnection(): bool
    {
        return $this->connection instanceof Connection;
    }
}
